# To change this template, choose Tools | Templates
# and open the template in the editor.

include Java
require '../../sennetworkserver/dist/sennetworkserver.jar'
#require 'lib/sennetworkserver.jar'

GREEN_MIN_TIME = 5
GREEN_MAX_TIME = 15
ORANGE_TIME = 6


class Lane  
  include java.util.Observer
  include_package "sennetworkserver"
  

  attr_reader :comboDef, :laneID
  attr_accessor :controller
 # @status #1 = red, 2 = orange, 3 = green

  def initialize id, combinationDef, priorityMultiplier    
    @comboDef = combinationDef  & ~(1 << id-1)
    @pMult = priorityMultiplier
    @carCount = 0    
    @laneID = id
    lane = LaneStore.lane @laneID
    @sensor100 = lane.sensor100m
    @sensor1 = lane.sensor1m
    @light = lane.traffic_light
    @sensor100.add_observer self
    @sensor1.add_observer self
    @light.add_observer self
    @last_time = Time.now #prevent nil in priority
    
  end

  def init cont
    @controller = cont
  end

  #time in seconds (float)
  def wait_time
    #red_state? ? Time.now - @last_time : 0
    Time.now - @last_time
  end

  def priority
    0 #default priority, should override
  end

  def update observable, sensor
    #useless
  end

  def orange_state?
    @light.get_state == TrafficLight::ORANGE
  end

  def red_state?
    @light.get_state == TrafficLight::RED
  end

  def green_state?
    @light.get_state == TrafficLight::GREEN
  end

  def update_state
    #@light.set_state TrafficLight::RED if orange_state? and (Time.now - @orange_state_time) > 15
    case
    when orange_state?
      if update_orange_condition
        @light.set_state TrafficLight::RED
        @last_time = Time.now
      end
    when green_state?
     # puts "#{@laneID} -> #{priority}"
      if update_green_condition
        @light.set_state TrafficLight::ORANGE
        @orange_state_time = Time.now
      end
    end
    @light.notify_observers
  end

  def set_open    
    @green_time = Time.now
    @light.set_state TrafficLight::GREEN
    @light.notify_observers
  end

#  def set_closed
#    if green_state?
#      @orange_state_time = Time.now
#      @light.set_state TrafficLight::ORANGE
#      @light.notify_observers
#      @last_time = Time.now
#    end
#  end

end

class CarLane < Lane
  @carCount = 0
  @@GREEN_TIME = 15
  @@ORANGE_TIME = 5

  def priority
    @pMult * @carCount * wait_time
  end

  def update observable, obj    
    if observable == @sensor100
      @last_time = Time.now if @carCount == 0
      @carCount+=1
      puts "car sensor 100m"
    elsif observable == @sensor1      
      @carCount-=1
      puts "car sensor 1m"
    end
  end

  private
  def update_green_condition
    #puts "#{Time.now - @green_time}, -> #{(Time.now - @green_time) > @@GREEN_TIME}"
    (priority == 0) or (Time.now - @green_time) > @@GREEN_TIME
  end
  
  def update_orange_condition
    (Time.now - @orange_state_time) > @@ORANGE_TIME
  end
end

class CyclistLane < Lane  
  @@GREEN_TIME = 5
  @@ORANGE_TIME = 0

  def initialize id, combinationDef, priorityMultiplier
    super id, combinationDef, priorityMultiplier
  end

  def priority
    1#@pMult * wait_time
  end

  def update observable, obj
    if observable == @sensor100
      @last_time = Time.now unless @button_pressed
      @button_pressed = true
    end
  end

  private  

  def update_green_condition
    (Time.now - @green_time) > @@GREEN_TIME
  end

  def update_orange_condition
    (Time.now - @orange_state_time) > @@ORANGE_TIME
  end
end

class ManualLane < Lane
  @button_pressed = false
  @@GREEN_TIME = 5
  @@ORANGE_TIME = 5

  def priority
    @button_pressed ? @pMult * wait_time : -1
  end

  def update observable, obj
    @button_pressed = true if observable == @sensor100
  end

  def set_open
    @button_pressed = false
    super
  end

  private

  def update_green_condition
    (Time.now - @green_time) > @@GREEN_TIME
  end

  def update_orange_condition
    (Time.now - @orange_state_time) > @@ORANGE_TIME
  end
end

#wraps multiple lanes into 1
class MultiLaneWrapper
  attr_reader :comboDef, :laneID

  def initialize lanes
    @lanes = Array.new lanes
    @comboDef = lanes[0].comboDef
    @laneID = lanes[0].laneID
  end

  def priority
    @lanes.inject(0) { |memo,obj| memo += obj.priority }
  end

  def set_open
    @lanes.each { |obj| obj.set_open }
  end

  def update_green_condition
    @lanes.inject(true) { |memo, obj| memo &&= obj.update_green_condition }
  end

  def update_orange_condition
    @lanes.inject(true) { |memo, obj| memo &&= obj.update_orange_condition }
  end

  def update_state
    @lanes.each { |obj| obj.update_state }
  end

  def green_state?
    @lanes.inject(true) { |memo, obj| memo &&= obj.green_state? }
  end

  def orange_state?
    @lanes.inject(true) { |memo, obj| memo &&= obj.orange_state? }
  end

  def red_state?
    @lanes.inject(true) { |memo, obj| memo &&= obj.red_state? }
  end

  
end

class Controller
  include_package "sennetworkserver"  

  def initialize
    @queue = java.util.ArrayList.new 28
    @comparator = PriorityComparator.new
    #@queue = java.util.TreeSet.new PriorityComparator.new
    @lanes = Array.new(28)    
    @server = NetworkServer.new 1337
      #new NetworkServer(Integer.parseInt(args[0]));

  end

  def add_lane lane
    #@lanes.push lane
    @queue.add lane
  end

  def get_lane_by_id(laneID)
    @queue.each{|l| return l if l.laneID == laneID}
  end

  def loop2
    if @server.is_connected
      auth_open = true
      byte = 0xFFFFFFFF
      java.util.Collections.sort @queue, @comparator      
      iterator = @queue.listIterator
      #keep everything that is orange or green
      while iterator.has_next
        lane = iterator.next
        lane.update_state
        byte &= lane.comboDef unless lane.red_state?
        #puts lane.priority
      end
      puts "---------------------"
      old_lane = nil
      iterator = @queue.listIterator
      while iterator.has_next
        lane = iterator.next
        auth_open = false if not old_lane.nil? and ((old_lane.priority - lane.priority) > lane.priority*2) and not lane.kind_of?(CyclistLane)
        puts  "lane #{lane.laneID} with priority #{lane.priority}" unless lane.priority == 0
        if auth_open and lane.priority > 0 and ((byte >> lane.laneID-1) & 1) == 1 and not lane.green_state?
          lane.set_open
          byte &= lane.comboDef        
        end
        old_lane = lane
      end

    end
    
  end

  def loop    
    if @server.is_connected
      java.util.Collections.sort @queue, @comparator
      byte = 0xFFFFFFFF
      puts "----------------"
      iterator = @queue.listIterator
    
     while iterator.has_next()
        lane = iterator.next()
        puts "lane #{lane.laneID}, priority: #{lane.priority}" unless lane.priority == 0
        if ((byte >> lane.laneID-1) & 1) == 1    #if the current lane can be added to the combination
          lane.set_open
          byte &= lane.comboDef #get all the possible combinations for this lane
          #puts "byte: #{byte}"
        else #lane can't be added to the current combination, make sure it's not open
         lane.set_closed
        end
      end
    end
  end

  def test_lane laneID
    while not @server.is_connected
    end
    puts "opening lane #{laneID}"
    lane = @queue.get(laneID-1)
    
    28.times do |i|
      if ((lane.comboDef >> i) & 1) == 1
        @queue.get(i).set_open
      end
    end
  end

  def close_all
    28.times do |i|
      @queue.get(i).set_closed
    end
  end
end

class PriorityComparator
  include java.util.Comparator

  def compare o1, o2
    o1.priority() < o2.priority() ? 1 : o1.priority() == o2.priority ? 0 : -1
  end
end


#create the lanes

c = Controller.new
a = Array.new
#28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
c.add_lane(CarLane.new(1, 0x28C7A57, 1))
c.add_lane(CarLane.new(2, 0x6959857, 1))
c.add_lane(CarLane.new(3, 0x6BDAB97, 1))
#c.add_lane(ManualLane.new(4, 0x3060C18, 1))
a.push(ManualLane.new(4, 0x3060C18, 4))
c.add_lane(CyclistLane.new(5, 0x3E7CF9F, 1))
c.add_lane(ManualLane.new(6, 0xD7D7B60, 1))
c.add_lane(ManualLane.new(7, 0xEBDB06B, 1))
c.add_lane(CarLane.new(8, 0x63D2B94, 1))
c.add_lane(CarLane.new(9, 0xACC2BB4, 1))
c.add_lane(CarLane.new(10, 0xED58BB4, 1))
#c.add_lane(ManualLane.new(11, 0x3060C18, 1))
a.push(ManualLane.new(11, 0x3060C18, 4))
c.add_lane(CyclistLane.new(12, 0xFF7CFBF, 1))
c.add_lane(ManualLane.new(13, 0xEBDB06B, 1))
c.add_lane(ManualLane.new(14, 0xED835F5, 1))
c.add_lane(CarLane.new(15, 0xE95CA31, 1))
c.add_lane(CarLane.new(16, 0x615DA56, 1))
c.add_lane(CarLane.new(17, 0xAC5DAF6, 1))
#c.add_lane(ManualLane.new(18, 0x3060C18, 1))
a.push(ManualLane.new(18, 0x3060C18, 4))
c.add_lane(CyclistLane.new(19, 0xBE7DFFF, 1))
c.add_lane(ManualLane.new(20, 0xED831F5, 1))
c.add_lane(ManualLane.new(21, 0xC18FAF6, 1))
c.add_lane(CarLane.new(22, 0xAE518F4, 1))
#c.add_lane(CarLane.new(23, 0xAED2B34, 1))
c.add_lane(CarLane.new(23, 0xAED2B30, 1))
c.add_lane(CarLane.new(24, 0x2ED7B57, 1))
#c.add_lane(ManualLane.new(25, 0x3060C18, 1))
a.push(ManualLane.new(25, 0x3060C18, 4))
c.add_lane(CyclistLane.new(26, 0x3EFFFDF, 1))
c.add_lane(ManualLane.new(27, 0xC18FAF6, 1))
c.add_lane(ManualLane.new(28, 0xC7D7B60, 1))
c.add_lane(MultiLaneWrapper.new(a))

Thread.abort_on_exception = true
t1 = Thread.new do
     #1.upto(28) do |i|
      #c.test_lane 3
     # sleep(10000000000000000000)
     #end
    
     while true
       c.loop2
       sleep(2)
     end
   end
   t1.join  